{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## _*Using Algorithm Concatenation in Qiskit Aqua*_\n",
    "\n",
    "This notebook demonstrates how to use the `Qiskit Aqua` library to realize algorithm concatenation. In particular, we experiment with chaining the executions of VQE and IQPE by first running VQE and then preparing IQPE's initial state using the variational form as produced by VQE upon its termination."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from qiskit import BasicAer\n",
    "from qiskit.transpiler import PassManager\n",
    "from qiskit.aqua import QuantumInstance\n",
    "from qiskit.aqua.algorithms import VQE, NumPyMinimumEigensolver, IQPE\n",
    "from qiskit.aqua.operators import WeightedPauliOperator\n",
    "from qiskit.circuit.library import TwoLocal\n",
    "from qiskit.aqua.components.optimizers import SPSA\n",
    "from qiskit.aqua.components.initial_states.var_form_based import VarFormBased"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here an Operator instance is created for our Hamiltonian, for which we are going to estimation the ground energy level. In this case the paulis are from a previously computed Hamiltonian for simplicity."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "pauli_dict = {\n",
    "    'paulis': [{\"coeff\": {\"imag\": 0.0, \"real\": -1.052373245772859}, \"label\": \"II\"},\n",
    "              {\"coeff\": {\"imag\": 0.0, \"real\": 0.39793742484318045}, \"label\": \"IZ\"},\n",
    "              {\"coeff\": {\"imag\": 0.0, \"real\": -0.39793742484318045}, \"label\": \"ZI\"},\n",
    "              {\"coeff\": {\"imag\": 0.0, \"real\": -0.01128010425623538}, \"label\": \"ZZ\"},\n",
    "              {\"coeff\": {\"imag\": 0.0, \"real\": 0.18093119978423156}, \"label\": \"XX\"}\n",
    "              ]\n",
    "}\n",
    "\n",
    "qubit_op = WeightedPauliOperator.from_dict(pauli_dict)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now use the Operator without regard to how it was created. First we will use the NumPyMinimumEigensolver to compute the reference ground energy level."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The reference ground energy level is -1.857275030202379.\n"
     ]
    }
   ],
   "source": [
    "result_reference = NumPyMinimumEigensolver(qubit_op).run()\n",
    "print('The reference ground energy level is {}.'.format(result_reference.eigenvalue.real))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Having established the reference ground energy, we next carry on with our experiment. First we configure a VQE algorithm instance. The idea is that we can set an termination condition such that the VQE instance returns rather quickly with a rough estimation result."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "VQE estimated the ground energy to be -1.7368866995757903.\n"
     ]
    }
   ],
   "source": [
    "random_seed = 0\n",
    "np.random.seed(random_seed)\n",
    "backend = BasicAer.get_backend('qasm_simulator')\n",
    "\n",
    "var_form_depth = 3\n",
    "var_form = TwoLocal(qubit_op.num_qubits, ['ry', 'rz'], 'cz', reps=var_form_depth)\n",
    "\n",
    "spsa_max_trials=10\n",
    "optimizer = SPSA(max_trials=spsa_max_trials)\n",
    "\n",
    "vqe = VQE(qubit_op, var_form, optimizer)\n",
    "\n",
    "quantum_instance = QuantumInstance(backend)\n",
    "result_vqe = vqe.run(quantum_instance)\n",
    "print('VQE estimated the ground energy to be {}.'.format(result_vqe.eigenvalue.real))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As previously indicated, the energy estimation result is rather rough--it is far from being an acceptable final estimation figure. But, it is close enough such that the accompanying variational form might be a reasonably good approximation to the ground eigenstate, which means the corresponding wave function can serve as the initial state for the IQPE execution that follows. We next prepare such an initial state."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "state_in = VarFormBased(var_form, result_vqe.optimal_point)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With the VQE-generated quantum state wave function serving as the chaining piece and prepared as initial state, we now go ahead with configuring and running an IQPE instance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Continuing with VQE's result, IQPE estimated the ground energy to be -1.8491663307965913.\n"
     ]
    }
   ],
   "source": [
    "num_time_slices = 1\n",
    "num_iterations = 6\n",
    "\n",
    "iqpe = IQPE(qubit_op, state_in, num_time_slices, num_iterations,\n",
    "            expansion_mode='suzuki', expansion_order=2,\n",
    "            shallow_circuit_concat=True)\n",
    "quantum_instance = QuantumInstance(backend, shots=100, seed_simulator=random_seed, seed_transpiler=random_seed)\n",
    "result_iqpe = iqpe.run(quantum_instance)\n",
    "print(\"Continuing with VQE's result, IQPE estimated the ground energy to be {}.\".format(\n",
    "    result_iqpe.eigenvalue.real))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As seen, the final ground energy estimation as produced by IQPE is much more accurate that the intermediate result as produced by VQE."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
